/** 
 *  Copyright (c) 2013  Ng Pan Wei
 *  
 *  Permission is hereby granted, free of charge, to any person 
 *  obtaining a copy of this software and associated documentation files 
 *  (the "Software"), to deal in the Software without restriction, 
 *  including without limitation the rights to use, copy, modify, merge, 
 *  publish, distribute, sublicense, and/or sell copies of the Software, 
 *  and to permit persons to whom the Software is furnished to do so, 
 *  subject to the following conditions: 
 *  
 *  The above copyright notice and this permission notice shall be 
 *  included in all copies or substantial portions of the Software. 
 *  
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
 *  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
 *  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
 *  SOFTWARE. 
 */

package jFeature.analysis;

import jFeature.lang.Trace;

import java.io.IOException;
import java.lang.reflect.Method;

public class Analysis {
	private ElementMap featureMap ;
	private ElementMap classMap ;
	private PackageMap packageMap ;
	private static Analysis singleton ;
	static {
		singleton = new Analysis() ;
	}
	public Analysis get() {
		return singleton ;
	}
	public Analysis() {
		featureMap = new ElementMap() ;
		classMap = new ElementMap() ;
		packageMap = new PackageMap() ;
	}
	public void read(String featurePackagePrefix,String implemenationPackagePrefix) 
			throws Exception {
		readFeatures(featurePackagePrefix);
		readClasses(implemenationPackagePrefix);		
	}
	public void read(String[] featurePackagePrefixes,String[] implemenationPackagePrefixes) 
			throws Exception {
		for(String prefixes : featurePackagePrefixes)
			readFeatures(prefixes);
		for(String prefixes : implemenationPackagePrefixes)
			readClasses(prefixes);		
			
	}
	private void readClasses(String implemenationPackagePrefix)
			throws ClassNotFoundException, IOException {
		Class<?>[] classes;
		classes = ClassScanner.getClasses(implemenationPackagePrefix.trim());
		for(Class<?> clazz : classes) {
			ElementNode node = new ElementNode(clazz) ;
			classMap.put(clazz,node) ;
			int count = 0 ;
			Trace trace = clazz.getAnnotation(Trace.class) ;
			count += traceToFeature(node, trace);
			for(Method method : clazz.getDeclaredMethods()) {
				trace = method.getAnnotation(Trace.class) ;
				count += traceToFeature(node, trace);
			}
			updatePackageNode(clazz, count) ;
		}
	}
	private int traceToFeature(ElementNode node, Trace trace) {
		int count = 0 ;
		if(trace==null)
			return count ;
		for(Class<?> feature : trace.value()) {
			ElementNode featureNode = featureMap.get(feature) ;
			if(feature==null)
				continue ;
			node.nodes.add(featureNode) ;
			featureNode.nodes.add(node) ;
			count ++ ;
		}
		return count ;
	}
	private void updatePackageNode(Class<?> clazz, int count) {
		String packageName = clazz.getName() ;
		int endIndex ;
		endIndex = packageName.lastIndexOf(".") ;
		while(endIndex>0) {
			packageName = packageName.substring(0, endIndex-1) ;
			PackageNode node = new PackageNode(packageName) ;
			packageMap.put(packageName, node) ;
			endIndex = packageName.lastIndexOf(".") ;
		}
	}
	private void readFeatures(String featurePackagePrefix)
			throws ClassNotFoundException, IOException {
		Class<?>[] classes = ClassScanner.getClasses(featurePackagePrefix.trim());
		for(Class<?> clazz : classes) {
			ElementNode node = new ElementNode(clazz) ;
			featureMap.put(clazz,node) ;
		}
	}

	public void analyze() {
		featureMap.analyze() ;
		classMap.analyze() ;
	}
	public ElementMap getFeatureMap() {
		return featureMap;
	}
	public ElementMap getClassMap() {
		return classMap;
	}
	public PackageMap getPackageMap() {
		return packageMap;
	}
	
}
